<template>
  <!-- 文章详情页右侧导航 -->
  <div class="blog-toc-container">
    <h2>目录</h2>
    <RightList :list="tocWithSelect" @select="handleSelect" />
  </div>
</template>

<script>
import RightList from "./RightList";
import { debounce } from "@/utils";
export default {
  components: {
    RightList,
  },
  props: {
    toc: {
      type: Array,
    },
  },
  data() {
    return {
      activeAnchor: "", // 当前选中项的id
    };
  },
  created() {
    this.setSelectDebounce = debounce(this.setSelect, 100);
    this.$bus.$on("mainScroll", this.setSelectDebounce); // 监听mainScroll事件, 设置处理函数为setSelectDebounce
  },
  destroyed() {
    // 组件销毁时候在事件总线中取消监听
    this.$bus.$off("mainScroll", this.setSelectDebounce);
  },
  computed: {
    // 根据toc属性以及activeAnchor得到带有isSelect数组的toc数组
    tocWithSelect() {
      // 递归
      const getToc = (toc = []) => {
        // if (!toc) {
        //   return;
        // }
        return toc.map((t) => {
          return {
            ...t,
            isSelect: t.anchor === this.activeAnchor,
            children: getToc(t.children),
          };
        });
      };
      return getToc(this.toc);
    },
    // 根据toc得到他们对应的h元素的数组
    doms() {
      const doms = [];
      // 递归
      const addToDoms = (toc) => {
        for (const t of toc) {
          doms.push(document.getElementById(t.anchor));
          if (t.children && t.children.length) {
            addToDoms(t.children);
          }
        }
      };
      addToDoms(this.toc);
      return doms;
    },
  },
  methods: {
    handleSelect(item) {
      location.hash = item.anchor;
    },
    // 设置activeAnchor为正确的值
    setSelect(scrollDom) {
      if (!scrollDom) {
        return;
      }
      const range = 200; // 选中的误差范围
      for (const dom of this.doms) {
        if (!dom) {
          // 当前dom可能未获取到
          continue;
        }
        // 判断当前dom是否应该被选中
        const top = dom.getBoundingClientRect().top; // 距顶部的高度
        const h1Top = document
          .querySelector(".blog-detail-container h1")
          .getBoundingClientRect().top;
        if (h1Top >= -50) {
          // 回到顶部则取消toc中的选中效果
          this.activeAnchor = "";
          return;
        }
        // 每次选中最后一个top为负的dom, 如果下一个top为正且在误差范围内, 则更新选中为该dom
        if (top <= -3) {
          this.activeAnchor = dom.id;
        } else {
          if (top <= range) {
            this.activeAnchor = dom.id;
          }
          return;
        }
      }
    },
  },
};
</script>

<style lang="less" scoped>
.blog-toc-container {
  h2 {
    font-weight: bold;
    letter-spacing: 2px;
    font-size: 1em;
    margin: 0;
    margin-bottom: 15px;
  }
}
</style>
